#ifndef __SKIN_MANAGER_H__
#define __SKIN_MANAGER_H__

#include <cstdint>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <mutex>
#include <memory>
#include <atomic>

#include "GameType.h"
#include "SimpleSingleton.hpp"
#include "GameLock.hpp"


namespace PocketSurvival
{
    struct SkinData
    {
        int32_t skinSize;                           // 皮肤的数据大小
        float destroyFactory;                       // 销毁时间，当到达0时，该皮肤的缓存将被释放
        char *skinBuff = nullptr;                   // 皮肤的缓存指针
    };

    class SkinsManager : public SimpleSingletonItem
    {
        friend class SimpleSingleton;
    private:
        float m_cumulativeTime = 0;

        std::hash<std::string> skinNameHash; 

        // 暂时考虑使用锁
        std::map<std::string, SkinData> m_skinDataMap;
        std::mutex skimDataMapMutex;

        // 服务器上存在的皮肤文件名称列表
        std::set<std::string> m_skinDirFileNames;
        SpinLock m_skinDirFileNameMapLock;

        // 皮肤请求，请求的皮肤可能会出现超时的问题
        std::map<std::string, float> m_requestSkinMap;
        SpinLock requsetSkinMapLock;

    private:
        SkinsManager();
        ~SkinsManager();
        SkinsManager(const SkinsManager &) = delete;
        SkinsManager(SkinsManager &&) = delete;
        SkinsManager &operator=(const SkinsManager &) = delete;
        SkinsManager &operator=(SkinsManager &&) = delete;

        /**
         * @brief 将皮肤名称字符串转成数字十六进制字符串，用于处理中文名称的问题
         * @param skinName 皮肤名称
         * @return 转换出来的字符串
        */
        std::string toHashSkinNameStr(const std::string &skinName);

    public:
        void update(float dt);

        /**
         * @brief 获取皮肤数据
         * @param skinName 皮肤名称
         * @return 皮肤数据，注： 若是 nullptr 则没有该皮肤数据
        */
        SkinData* getSkinData(const std::string &skinName);

        /**
         * @brief 添加皮肤数据，若是本地不存在的皮肤，还会进行保存，若是已经存在的皮肤则会报错
         * @param skinName 皮肤名称
         * @param skinData 皮肤数据
        */
        void addSkinData(const std::string &skinName, SkinData &skinData);

        /**
         * @brief 向特定的客户端请求皮肤文件
         * @param client 客户端对象
         * @param skinName 皮肤名称
        */
        void requsetSkin(ClientPtr &client, const std::string& skinName);

        /**
         * @brief 判断某个皮肤是否正在被请求上传中
         * @param skinName 皮肤名称
        */
        bool haveRequsetSkin(const std::string& skinName);

        /**
         * @brief 判断某个皮肤是否存在
         * @param skinName 皮肤名称
        */
        bool haveSkin(const std::string &skinName);

        void loadGame();
    };


} // namespace PocketSurvival


#endif

